hive insert overwrite directory 问题排查

图片看不到的可以看我的CSDN的博客
hive insert overwrite directory 问题排查

一、问题描述

线上某条hive语句出现错误,hive语句如下

insert overwrite directory '/tmp/data_works_server/41109/904723/1547802020/' STORED AS ORC 
select 20190117, device_model, meios_version, server_id, ev.type, sum(ev.count) from rom lateral view explode(events) events as ev where date_p=20181031 and app_key_p='D1AF3AB43299858F' and ev.name='reboot' group by device_model, meios_version, server_id, ev.type;

经过简单测试,发现以下3点现象:

  1. 去掉STORED AS ORC后语句正常执行
  2. 只查询某些字段的话可以正常执行,查询某些表也可以正常执行
  3. 在查询的最后加上limit xxx后语句可以正常执行,xxx可以是任意数字

只看上面两点的话很可能会觉的是某些数据造成的问题,但再加上第三点的话,又应该不是数据问题。后面进行问题定位

二、问题定位

为了方便描述问题,我们先建立一个新表

create table test(id string);

然后看一下类似语句的执行计划

explain insert overwrite directory '/tmp/test/' stored as orc select * from test;

最后可以看到执行计划

STAGE DEPENDENCIES:
  Stage-1 is a root stage
  Stage-6 depends on stages: Stage-1 , consists of Stage-3, Stage-2, Stage-4
  Stage-3
  Stage-0 depends on stages: Stage-3, Stage-2, Stage-5
  Stage-2
  Stage-4
  Stage-5 depends on stages: Stage-4

STAGE PLANS:
  Stage: Stage-1
    Spark
      DagName: hadoopuser_20190122170810_884746eb-f85f-49c4-b0d1-57bd5dfe1219:2
      Vertices:
        Map 1
            Map Operator Tree:
                TableScan
                  alias: test
                  Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
                  Select Operator
                    expressions: id (type: string)
                    outputColumnNames: _col0
                    Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
                    File Output Operator
                      compressed: false
                      Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
                      table:
                          input format: org.apache.hadoop.mapred.TextInputFormat
                          output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
                          serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde

  Stage: Stage-6
    Conditional Operator

  Stage: Stage-3
    Move Operator
      files:
          hdfs directory: true
          destination: hdfs://rangercluster/tmp/test/.hive-staging_hive_2019-01-22_17-08-10_712_8680878334963783411-1/-ext-10000

  Stage: Stage-0
    Move Operator
      files:
          hdfs directory: true
          destination: /tmp/test

  Stage: Stage-2
    Spark
      DagName: hadoopuser_20190122170810_884746eb-f85f-49c4-b0d1-57bd5dfe1219:3
      Vertices:
        Spark Merge File Work
            Map Operator Tree:
                TableScan
                  File Output Operator
                    compressed: false
                    table:
                        input format: org.apache.hadoop.mapred.TextInputFormat
                        output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
                        serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde

  Stage: Stage-4
    Spark
      DagName: hadoopuser_20190122170810_884746eb-f85f-49c4-b0d1-57bd5dfe1219:3
      Vertices:
        Spark Merge File Work
            Map Operator Tree:
                TableScan
                  File Output Operator
                    compressed: false
                    table:
                        input format: org.apache.hadoop.mapred.TextInputFormat
                        output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
                        serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde

  Stage: Stage-5
    Move Operator
      files:
          hdfs directory: true
          destination: hdfs://rangercluster/tmp/test/.hive-staging_hive_2019-01-22_17-08-10_712_8680878334963783411-1/-ext-10000

上面的计划生成DAG图大概是这样的:

[图片上传失败...(image-78dce5-1548166918410)]

整个语句执行的流程大概如下:

  1. Stage-1 执行查询的语句,之后把结果写到某个临时目录A。
  2. Stage-6根据生成的临时目录A的相关信息,判断是否要merge,或者还是直接move,再或者merge and move。merge的过程需要读取临时目录A下的所有数据然后再写入到新的一个临时目录B
  3. Stage-0直接将临时目录的数据到move到目标目录下,也就是最终路径 /tmp/test

这个流程有个很关键的地方,就是是否需要merge,经过排查,发现出错的语句在执行结束后全部都经过了merge,也就是执行了Stage-2

再回头看下那个执行计划,我们会发现一个问题:

  Stage: Stage-1
        ...
                    File Output Operator
                      compressed: false
                      Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
                      table:
                          input format: org.apache.hadoop.mapred.TextInputFormat
                            # 这里是以OrcOutputFormat输出的
                          output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
                          serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
    ...
  Stage: Stage-2
    Spark
      DagName: hadoopuser_20190122170810_884746eb-f85f-49c4-b0d1-57bd5dfe1219:3
      Vertices:
        Spark Merge File Work
            Map Operator Tree:
                TableScan
                  File Output Operator
                    compressed: false
                    table:
                    # merge的这里是用 TextInputFormat 去读取
                        input format: org.apache.hadoop.mapred.TextInputFormat
                        output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
                        serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde

hive的执行计划很清楚的指出,hive将查询结果输出到某个临时目录时,用的是OrcOutputFormat,但是Stage-2要去merge的时候却是用TextInputFormat,这明显是不对的

为了对比,我们创建一个orc的表来验证一下。

create table orctest(id string) stored as orc;

然后往这个表查数据

explain insert into orctest select * from test;

获得执行计划

STAGE DEPENDENCIES:
  Stage-1 is a root stage
  Stage-7 depends on stages: Stage-1 , consists of Stage-4, Stage-3, Stage-5
  Stage-4
  Stage-0 depends on stages: Stage-4, Stage-3, Stage-6
  Stage-2 depends on stages: Stage-0
  Stage-3
  Stage-5
  Stage-6 depends on stages: Stage-5

STAGE PLANS:
  Stage: Stage-1
    Spark
      DagName: hadoopuser_20190122171054_54c5e0e6-d662-4239-b527-9f8e3c2d5bcd:4
      Vertices:
        Map 1
            Map Operator Tree:
                TableScan
                  alias: test
                  Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
                  Select Operator
                    expressions: id (type: string)
                    outputColumnNames: _col0
                    Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
                    File Output Operator
                      compressed: false
                      Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
                      table:
                          input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
                          output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
                          serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
                          name: default.orctest

  Stage: Stage-7
    Conditional Operator

  Stage: Stage-4
    Move Operator
      files:
          hdfs directory: true
          destination: hdfs://rangercluster/user/hive/warehouse/orctest/.hive-staging_hive_2019-01-22_17-10-54_801_6594738283174520641-1/-ext-10000

  Stage: Stage-0
    Move Operator
      tables:
          replace: false
          table:
              input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
              output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
              serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
              name: default.orctest

  Stage: Stage-2
    Stats-Aggr Operator

  Stage: Stage-3
    Spark
      DagName: hadoopuser_20190122171054_54c5e0e6-d662-4239-b527-9f8e3c2d5bcd:5
      Vertices:
        Spark Merge File Work
          Merge File Operator
            Map Operator Tree:
                ORC File Merge Operator
            merge level: stripe
            input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat

  Stage: Stage-5
    Spark
      DagName: hadoopuser_20190122171054_54c5e0e6-d662-4239-b527-9f8e3c2d5bcd:5
      Vertices:
        Spark Merge File Work
          Merge File Operator
            Map Operator Tree:
                ORC File Merge Operator
            merge level: stripe
            input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat

  Stage: Stage-6
    Move Operator
      files:
          hdfs directory: true
          destination: hdfs://rangercluster/user/hive/warehouse/orctest/.hive-staging_hive_2019-01-22_17-10-54_801_6594738283174520641-1/-ext-10000

我们可以看到这时对应的inputformat和outputformat是正常的

也就是hive在执行insert overwrite directory '' stored as xxx时会有问题。经测试,确实无论是orc、parquet、avro都会导出失败

三、问题总结

Hive在执行insert overwrite directory '' stored as xxx selext ...进行导出时,如果导出的文件格式不是textfile,并且select查询生成的临时文件需要再次merge,就会导致merge的时候出现异常,因为merge时使用的inputFormat是默认的TextInputFormat,这应该是hive的一个bug。

这里对第一节的3点现象为什么可以成功执行做一个解释:

  1. 去掉stored as orc后,存储格式是textfile,采用TextInputFormat可以正常进行merge,因此不会有问题
  2. 改成某些字段后,虽然sql可以正常执行,这可能只是merge的过程中没有报错,但是最后得到的数据应该都是有问题的,目前可以确认orc、parquet等输出后的数据都是有问题的。比如查询两个字段以orc格式输出到目录A,虽然整个语句执行过程中没有发生任何问题,但是再去用orc查询目录A下面的数据时,发现两个列的数据都是0。
  3. 由于加了limit之后查询只会输出一个文件,因此没有涉及到merge的过程,走的是Stage-3,因此inputFormat导致的问题不会影响数据导出。

四、解决方案

  1. 建立一张临时外部表,location指向目标目录。之后再写入,比如要写一个这样的语句
insert overwrite directory '/tmp/test/' stored as orc select id from test;

可以建立一下新的临时外部表

create external table tmptest(id string) stored as orc location '/tmp/test/';

再导出

insert into tmptest select id from test;
  1. 修改hive源码

看了一圈源码,发现是hive在生成逻辑计划时,没有为这种写入源是目录的场景设置对应的inputFormat。我们可以在hive源码的SemanticAnalyzer#genFileSinkPlan()方法中修改FileSinkDesc的inputFormat。—— 可以从queryBlock中获取正确的inputformat,因为在queryBlock中已经持有正确的InputFormat了

改的时候要注意不要影响到其他的功能,尽量在case QBMetaData.DEST_DFS_FILE语句块中修改

改完代码重新编译打包,然后替换一下hive-exec-xxx.jar重启hiveserver2就可以了。

五、一些题外话

关于Conditional Operator以及小文件合并的一些东西。

上文说的Conditional Operator其实就是一个抉择要不要进行merge的过程,我们在提交hive语句,hive日志会输出具体选择哪个Stage进行执行。

在这里插入图片描述

从上图可以看到hive在执行完查询语句后选择了Stage-2执行。

Stage-6如何抉择的相关代码在hive ql模块的ConditionalResolverMergeFiles中,主要是根据一些配置以及查询生成的文件信息判断要选择哪个Stage执行。

比如spark查询job跑完后,会根据hive.merge.sparkfiles配置是否为true判断是否要开启小文件合并,之后再根据hive.merge.size.per.taskhive.merge.smallfiles.avgsize以及当前生成的临时目录中的小文件信息,判断是否开启小文件合并。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容